package xin.ryven.workflow.service.impl;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import xin.ryven.workflow.model.ProcessRequest;
import xin.ryven.workflow.repository.ProcessRequestRepository;
import xin.ryven.workflow.security.UserDetailsImpl;
import xin.ryven.workflow.service.ProcessService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.camunda.bpm.engine.repository.DeploymentBuilder;
import org.camunda.bpm.engine.RepositoryService;
import xin.ryven.workflow.model.FormField;
import xin.ryven.workflow.repository.FormFieldRepository;
import xin.ryven.workflow.model.ApprovalHistory;
import xin.ryven.workflow.repository.ApprovalHistoryRepository;
import org.springframework.security.core.context.SecurityContextHolder;
import xin.ryven.workflow.model.User;
import xin.ryven.workflow.repository.UserRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import xin.ryven.workflow.model.FormConfig;
import xin.ryven.workflow.repository.FormConfigRepository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;

@Service
@Slf4j
public class ProcessServiceImpl implements ProcessService {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ProcessRequestRepository processRequestRepository;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FormFieldRepository formFieldRepository;

    @Autowired
    private ApprovalHistoryRepository approvalHistoryRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private FormConfigRepository formConfigRepository;

    @Override
    public ProcessRequest getProcessById(Long id) {
        return processRequestRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Process request not found"));
    }

    @Override
    public List<ProcessRequest> getMyRequests(Long userId) {
        List<ProcessRequest> requests = processRequestRepository.findByRequesterId(userId);
        
        // 获取发起人信息
        for (ProcessRequest request : requests) {
            userRepository.findById(request.getRequesterId()).ifPresent(requester -> request.setRequesterName(requester.getFullName()));
        }
        
        return requests;
    }

    @Override
    public List<Task> getMyTasks(String userId) {
        return taskService.createTaskQuery()
                .taskAssignee(userId)
                .active()
                .orderByTaskCreateTime()
                .desc()
                .list();
    }

    @Override
    public Map<String, Object> getTaskFormData(String taskId) {
        // 获取任务
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();

        if (task == null) {
            throw new RuntimeException("Task not found: " + taskId);
        }

        // 获取流程定义ID
        String processDefinitionId = task.getProcessDefinitionId();

        // 获取任务节点的表单配置
        List<FormField> fields = formFieldRepository
                .findByProcessDefinitionIdAndTaskDefinitionKeyOrderByOrderIndex(
                        processDefinitionId,
                        task.getTaskDefinitionKey()
                );

        Map<String, Object> formData = new HashMap<>();
        formData.put("fields", fields);
        return formData;
    }

    @Override
    public Map<String, Object> getProcessVariables(String processInstanceId) {
        return runtimeService.getVariables(processInstanceId);
    }

    @Override
    @Transactional
    public void completeTask(String taskId, Map<String, Object> variables) {
        // 获取任务
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();

        if (task == null) {
            throw new RuntimeException("Task not found: " + taskId);
        }

        // 获取流程实例ID
        String processInstanceId = task.getProcessInstanceId();

        // 获取当前用户
        UserDetailsImpl userDetails = (UserDetailsImpl) SecurityContextHolder
                .getContext()
                .getAuthentication()
                .getPrincipal();

        // 保存审批记录
        ApprovalHistory history = new ApprovalHistory();
        history.setProcessInstanceId(processInstanceId);
        history.setTaskId(taskId);
        history.setTaskName(task.getName());
        history.setApproverId(userDetails.getId());
        history.setApproverName(userDetails.getFullName());
        history.setApproved((Boolean) variables.get("approved"));
        history.setComment((String) variables.get("comment"));
        approvalHistoryRepository.save(history);

        // 完成任务
        taskService.complete(taskId, variables);

        // 检查流程是否结束
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        // 更新流程请求状态
        ProcessRequest processRequest = processRequestRepository
                .findByProcessInstanceId(processInstanceId)
                .orElseThrow(() -> new RuntimeException("Process request not found"));

        if (processInstance == null) {
            // 流程已结束
            processRequest.setStatus("COMPLETED");
        } else {
            // 获取当前任务
            Task currentTask = taskService.createTaskQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();

            if (currentTask != null) {
                processRequest.setStatus(currentTask.getName());
            }
        }

        processRequestRepository.save(processRequest);
    }

    @Override
    public void saveProcessDefinition(String key, String bpmnXml) {
        try {
            // 创建部署构建器
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                    .name(key)
                    .addString(key + ".bpmn", bpmnXml);

            // 执行部署
            deploymentBuilder.deploy();
            log.info("Process definition saved successfully: {}", key);
        } catch (Exception e) {
            log.error("Failed to save process definition: {}", key, e);
            throw new RuntimeException("保存流程定义失败", e);
        }
    }

    @Override
    public void updateProcessDefinition(String id, String key, String bpmnXml) {
        try {
            // 先查找流程定义
            org.camunda.bpm.engine.repository.ProcessDefinition processDefinition = repositoryService
                    .createProcessDefinitionQuery()
                    .processDefinitionId(id)  // 使用流程定义ID而不是部署ID
                    .singleResult();

            if (processDefinition == null) {
                throw new RuntimeException("找不到原流程定义");
            }

            // 删除旧版本（级联删除相关的所有数据）
            repositoryService.deleteDeployment(processDefinition.getDeploymentId(), true);

            // 部署新版本
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                    .name(key)
                    .source("update")
                    .enableDuplicateFiltering(false)
                    .addString(key + ".bpmn", bpmnXml);

            // 执行部署
            org.camunda.bpm.engine.repository.Deployment newDeployment = deploymentBuilder.deploy();

            log.info("Process definition updated successfully: {} (old deployment: {}, new deployment: {})",
                    key, processDefinition.getDeploymentId(), newDeployment.getId());
        } catch (Exception e) {
            log.error("Failed to update process definition: {}", key, e);
            throw new RuntimeException("更新流程定义失败: " + e.getMessage(), e);
        }
    }

    @Override
    public String getProcessDefinitionXml(String deploymentId) {
        try {
            // 根据部署ID获取最新的流程定义
            org.camunda.bpm.engine.repository.ProcessDefinition processDefinition = repositoryService
                    .createProcessDefinitionQuery()
                    .deploymentId(deploymentId)
                    .latestVersion()
                    .singleResult();

            if (processDefinition == null) {
                throw new RuntimeException("找不到流程定义");
            }

            // 获取XML内容
            InputStream resourceStream = repositoryService.getResourceAsStream(
                    processDefinition.getDeploymentId(),
                    processDefinition.getResourceName()
            );

            return new String(resourceStream.readAllBytes(), StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("Failed to get process definition XML", e);
            throw new RuntimeException("获取流程定义XML失败", e);
        }
    }

    @Override
    public Map<String, Object> getProcessFormConfig(String processDefinitionId) {
        return formConfigRepository.findById(processDefinitionId)
                .map(config -> {
                    try {
                        return objectMapper.readValue(config.getConfig(), Map.class);
                    } catch (Exception e) {
                        log.error("Error parsing form config", e);
                        return new HashMap<String, Object>();
                    }
                })
                .orElse(new HashMap<>());
    }

    @Override
    public void saveProcessFormConfig(String processDefinitionId, List<FormField> fields) {
        FormConfig config = formConfigRepository.findById(processDefinitionId)
                .orElse(new FormConfig());

        config.setProcessDefinitionId(processDefinitionId);
        config.setConfig(JSON.toJSONString(Map.of("fields", fields)));

        if (config.getCreatedAt() == null) {
            config.setCreatedAt(LocalDateTime.now());
        }
        config.setUpdatedAt(LocalDateTime.now());

        formConfigRepository.save(config);
    }

    @Override
    public List<ApprovalHistory> getProcessHistory(String processInstanceId) {
        return approvalHistoryRepository.findByProcessInstanceIdOrderByCreatedAtDesc(processInstanceId);
    }

    @Override
    @Transactional
    public ProcessRequest startProcess(String processDefinitionId, Map<String, Object> variables) {
        // 创建流程请求记录
        ProcessRequest request = new ProcessRequest();
        request.setStatus("PENDING");
        request.setCreatedAt(LocalDateTime.now());

        // 设置发起人
        Long initiatorId = (Long) variables.get("initiator");
        request.setRequesterId(initiatorId);

        // 启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(
                processDefinitionId,
                variables
        );

        // 更新请求记录
        request.setProcessInstanceId(processInstance.getId());
        return processRequestRepository.save(request);
    }

    @Override
    public Map<String, Object> getProcessInstanceDetails(String processInstanceId) {
        Map<String, Object> details = new HashMap<>();
        
        // 获取流程请求信息
        ProcessRequest request = processRequestRepository.findByProcessInstanceId(processInstanceId)
                .orElseThrow(() -> new RuntimeException("Process request not found"));
                
        // 获取发起人信息
        userRepository.findById(request.getRequesterId())
                .ifPresent(requester -> details.put("requesterName", requester.getFullName()));
        
        details.put("createdAt", request.getCreatedAt());
        
        // 获取表单配置以确定字段顺序
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        
        if (processInstance != null) {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(processInstance.getProcessDefinitionId())
                    .singleResult();
            details.put("processName", processDefinition.getName());
            
            // 获取表单配置
            Map<String, Object> formConfig = getProcessFormConfig(processDefinition.getId());
            List<Map<String, Object>> fields = (List<Map<String, Object>>) formConfig.get("fields");
            
            // 获取流程变量并按表单配置顺序重组
            Map<String, Object> variables = runtimeService.getVariables(processInstanceId);
            Map<String, Object> orderedFormData = new LinkedHashMap<>();
            if (fields != null) {
                fields.forEach(field -> {
                    String fieldId = (String) field.get("fieldId");
                    if (variables.containsKey(fieldId)) {
                        orderedFormData.put(fieldId, variables.get(fieldId));
                    }
                });
            }
            details.put("formData", orderedFormData);
        }
        
        return details;
    }
} 